// Package xpath impliments more advanced xpath expressions than "gopkg.in/xmlpath.v2" currently does.
package xpath

import (
	"bytes"

	"code.google.com/awwwe-browser/data"
)

const (
	countStartString  string = "count"
	existsStartString string = "exists"
)

const (
	parenStartRune   rune = '('
	parenEndRune     rune = ')'
	bracketStartRune rune = '['
	bracketEndRune   rune = ']'
	slashRune        rune = '/'
	dotRune          rune = '.'
)

var (
	notEqualRunes         []rune = bytes.Runes([]byte("!="))
	lessThanRunes         []rune = bytes.Runes([]byte("<"))
	greaterThanRunes      []rune = bytes.Runes([]byte(">"))
	lessThanEqualRunes    []rune = bytes.Runes([]byte("<="))
	greaterThanEqualRunes []rune = bytes.Runes([]byte(">="))
	countStartRunes       []rune = bytes.Runes([]byte("count"))
	existsStartRunes      []rune = bytes.Runes([]byte("exists"))
	andRunes              []rune = bytes.Runes([]byte("and"))
	orRunes               []rune = bytes.Runes([]byte("or"))
	emptyRunes            []rune = make([]rune, 0, 0)
)

type comparisonType int

const (
	_ comparisonType = iota
	comparisonTypeNil
	comparisonTypeEqual
	comparisonTypeNotEqual
	comparisonTypeLessThan
	comparisonTypeGreaterThan
	comparisonTypeLessThanEqual
	comparisonTypeGreaterThanEqual
)

// remove paren wrap
// converts " ( ( /p ) ) " to "/p"
func unwrapRunes(path []rune) []rune {
	var count, i, length int
	i = 0
	length = len(path)
	// trim prefix
	for i < length {
		if path[i] == ' ' || path[i] == '\t' || path[i] == '\f' || path[i] == '\r' || path[i] == '\n' || path[i] == '\v' || path[i] == '\b' {
			i++
		} else {
			break
		}
	}
	if i > 0 {
		path = path[i:]
		length = len(path)
	}
	// trim suffix
	i = length - 1
	for i > 0 {
		if path[i] == ' ' || path[i] == '\t' || path[i] == '\f' || path[i] == '\r' || path[i] == '\n' || path[i] == '\v' || path[i] == '\b' {
			i--
		} else {
			break
		}
	}
	if i < length-1 {
		path = path[:i+1]
		length = len(path)
	}
	// check for wrap
	if len(path) <= 1 || (path[0] != data.LeftParenRune || path[length-1] != data.RightParenRune) {
		// not wrapped
		return path
	}
	// this could be wrapped
	count = 1
	i = 1
	for i < length {
		if path[i] == data.LeftParenRune {
			count++
		} else if path[i] == data.RightParenRune {
			count--
		}
		i++
	}
	// at the end of the path
	// test for wrapped
	if count == 0 {
		// wrapped in parens
		path = path[1 : length-1]
		// could be double wrapped
		return unwrapRunes(path)
	}
	return path
}
